#ifndef _OCPP_LogStream_H__
#define _OCPP_LogStream_H__

#include "Prerequisites.h"

namespace OCPP
{
    static constexpr size_t kSmallBuffer{ 4000 };
    static constexpr size_t kLargeBuffer{ 4000 * 1000 };

    template <int SIZE>
    class _MainExport FixedBuffer
    {
    public:
        FixedBuffer() : cur_(data_)
        {
        }

        ~FixedBuffer()
        {
        }

        bool append(const char* /*restrict*/ buf, size_t len)
        {
            if ((size_t)(avail()) > len)
            {
                memcpy(cur_, buf, len);
                cur_ += len;
                return true;
            }
            return false;
        }

        const char* data() const
        {
            return data_;
        }
        int length() const
        {
            return static_cast<int>(cur_ - data_);
        }

        // write to data_ directly
        char* current()
        {
            return cur_;
        }
        int avail() const
        {
            return static_cast<int>(end() - cur_);
        }
        void add(size_t len)
        {
            cur_ += len;
        }

        void reset()
        {
            cur_ = data_;
        }
        void zeroBuffer()
        {
            memset(data_, 0, sizeof(data_));
        }

        std::string toString() const
        {
            return std::string(data_, length());
        }
    private:
        const char* end() const
        {
            return data_ + sizeof data_;
        }

        char data_[SIZE];
        char* cur_;
    };

	class _MainExport LogStream
	{
        using self = LogStream;
    public:
        using Buffer = FixedBuffer<kSmallBuffer>;
        self& operator<<(bool v)
        {
            append(v ? "1" : "0", 1);
            return *this;
        }

        self& operator<<(short);
        self& operator<<(unsigned short);
        self& operator<<(int);
        self& operator<<(unsigned int);
        self& operator<<(long);
        self& operator<<(unsigned long);
        self& operator<<(const long long&);
        self& operator<<(const unsigned long long&);

        self& operator<<(float& v)
        {
            *this << static_cast<double>(v);
            return *this;
        }
        self& operator<<(const double&);
        self& operator<<(const long double& v);

        self& operator<<(char v)
        {
            append(&v, 1);
            return *this;
        }

        template <int N>
        self& operator<<(const char(&buf)[N])
        {
            assert(strnlen(buf, N) == N - 1);
            append(buf, N - 1);
            return *this;
        }

        self& operator<<(char* str)
        {
            if (str)
            {
                append(str, strlen(str));
            }
            else
            {
                append("(null)", 6);
            }
            return *this;
        }

        self& operator<<(const char* str)
        {
            if (str)
            {
                append(str, strlen(str));
            }
            else
            {
                append("(null)", 6);
            }
            return *this;
        }

        self& operator<<(const unsigned char* str)
        {
            return operator<<(reinterpret_cast<const char*>(str));
        }

        self& operator<<(const void*);

        self& operator<<(const std::string& v)
        {
            append(v.c_str(), v.size());
            return *this;
        }

        const char* bufferData() const;

        size_t bufferLength() const;

        void resetBuffer();

        void append(const char* data, size_t len);
    private:
        template <typename T>
        void formatInteger(T);

        Buffer buffer_;
        std::string exBuffer_;
	};
}

#endif